﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Renci.SshNet;

namespace PurpleKnife.Library
{
    class AsyncSwitchCommand
    {
        private pkSwitch _switch;
        private Thread cmdThread;
        private SshClient sshCli;
        private bool _isConnected;
        private bool _isExecuting;
        private int _commandTimeout = 30;

        public event EventHandler<CommandCompletedEventArgs> ReadCompleted;

        public void Connect(pkSwitch Switch)
        {
            if (_isConnected)
            {
                throw new InvalidOperationException("AsyncSwitchCommand is already connected.");
            }
            else
            {
                
                // switch information
                _switch = Switch;

                // connection information
                string host = _switch.SSHHost;
                int port = _switch.SSHPort;
                string user = _switch.SSHUserName;
                string pwd = _switch.SSHPassword.ToString();

                // create and setup SSH client object
                sshCli = new SshClient(host, port, user, pwd);
                sshCli.HostKeyReceived += new EventHandler<Renci.SshNet.Common.HostKeyEventArgs>(sshCli_HostKeyReceived);
                sshCli.ErrorOccurred += new EventHandler<Renci.SshNet.Common.ExceptionEventArgs>(sshCli_ErrorOccurred);
                sshCli.ConnectionInfo.Timeout = Switch.SSHConnectionTimeout;
                sshCli.ConnectionInfo.RetryAttempts = 1;

                // build connection
                sshCli.Connect();

                // connection successfull
                _isConnected = true;
            }    
        }

        public void Disconnect()
        {
            if (_isConnected)
            {
                sshCli.Disconnect();
            }
            else
            {
                throw new InvalidOperationException("AsyncSwitchCommand is not connected.");
            }
        }

        
        public void Execute(string Command)
        {
            cmdThread = new Thread(ExecuteOnSwitch);
            cmdThread.IsBackground = true;
            cmdThread.Start(Command);
        }

        
        private void ExecuteOnSwitch(object Command)
        {
            if (_isConnected)
            {
                if (_isExecuting)
                {
                    throw new InvalidOperationException("AsyncSwitchCommand is currently executing.");
                }
                else
                {
                    _isExecuting = true;
                    string Cmd = (string)Command;
                    string CmdOutput = "";

                    // create completed event args
                    CommandCompletedEventArgs ea = new CommandCompletedEventArgs();

                    try
                    {
                        // execute command
                        SshCommand cmdObj = sshCli.CreateCommand(Cmd);
                        cmdObj.CommandTimeout = TimeSpan.FromSeconds(_commandTimeout);
                        CmdOutput = cmdObj.Execute();
                        

                        // get output
                        ea.CommandOutput = CmdOutput;
                        ea.CommandSuccessful = true;

                    }
                    catch (Exception ex)
                    {
                        ea.CommandOutput = ex.Message;
                        ea.CommandSuccessful = false;
                    }

                    _isExecuting = false;

                    // raise completed event
                    OnCommandCompleted(this, ea);
                }
            }
            else
            {
                throw new InvalidOperationException("AsyncSwitchCommand is not connected.");
            }
        }

        // RaiseEvent helper
        protected virtual void OnCommandCompleted(object sender, CommandCompletedEventArgs e)
        {
            // raise the event
            EventHandler<CommandCompletedEventArgs> handler = ReadCompleted;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public class CommandCompletedEventArgs : EventArgs
        {
            public bool CommandSuccessful { set; get; }
            public string CommandOutput { set; get; }
        }

        // handle SSH error
        private void sshCli_ErrorOccurred(object sender, Renci.SshNet.Common.ExceptionEventArgs e)
        {
            // TODO: implement
            ;
        }

        // compare SSH host key fingerprints
        private void sshCli_HostKeyReceived(object sender, Renci.SshNet.Common.HostKeyEventArgs e)
        {
            // fingerprint received
            string fingerprintReceived = BitConverter.ToString(e.FingerPrint);
            // fingerprint stored
            string fingerprintStored = _switch.SSHKeyFingerprint;
            // can trust if received = stored
            e.CanTrust = (fingerprintReceived == fingerprintStored);
        }
    }
}
